--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Node / ReticulumProjects / MeshChatX.git / files / meshchatx / src / backend / battery_usage_estimate.py
Displaying Raw • Download
meshchatx/src/backend/battery_usage_estimate.py c0b0ddffd735a0d007c1be9a5e8563ec42fdafc0 (c0b0ddff) Text, 6.22 KB
T8b949e# SPDX-License-Identifier: 0BSD
T8b949e"""Estimate MeshChatX battery drain from process CPU time.
OS APIs rarely expose per-app battery without privileged permissions.
This module derives a conservative estimate from cumulative process CPU
time versus wall-clock uptime so About can show app-level usage instead
of only the host pack percentage.
"""
Tff7b72from T7ee787__future__ Tff7b72import Te6edf3annotations
Tff7b72import T7ee787os
Tff7b72import T7ee787time
Tff7b72from T7ee787typing Tff7b72import Te6edf3Any
T8b949e# Rough calibration: one busy logical core ~ this many battery percent per hour
T8b949e# on a typical phone or thin laptop. Tuned to be conservative and capped.
Te6edf3_ONE_CORE_PERCENT_PER_HOUR Tff7b72= T79c0ff10.0
Te6edf3_MAX_PERCENT_PER_HOUR Tff7b72= T79c0ff40.0
Te6edf3_MIN_UPTIME_FOR_RATE_S Tff7b72= T79c0ff30.0
Tff7b72def Td2a8ff_clampTb4b4b4(Te6edf3valueTb4b4b4: Tffa657floatTb4b4b4, Te6edf3lowTb4b4b4: Tffa657floatTb4b4b4, Te6edf3highTb4b4b4: Tffa657floatTb4b4b4) Tff7b72-Tff7b72> Tffa657floatTb4b4b4:
Tff7b72return Tffa657maxTb4b4b4(Te6edf3lowTb4b4b4, Tffa657minTb4b4b4(Te6edf3highTb4b4b4, Te6edf3valueTb4b4b4)Tb4b4b4)
Tff7b72def Td2a8ffdrain_intensityTb4b4b4(Te6edf3estimated_percent_per_hourTb4b4b4: Tffa657float Tff7b72| Tff7b72NoneTb4b4b4) Tff7b72-Tff7b72> Tffa657str Tff7b72| Tff7b72NoneTb4b4b4:
T8b949e"""Map an estimated %/hr rate to a coarse intensity label."""
Tff7b72if Te6edf3estimated_percent_per_hour Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72return Tff7b72None
Tff7b72if Te6edf3estimated_percent_per_hour Tff7b72< T79c0ff0.5Tb4b4b4:
Tff7b72return Ta5d6ff"Ta5d6fflowTa5d6ff"
Tff7b72if Te6edf3estimated_percent_per_hour Tff7b72< T79c0ff2.0Tb4b4b4:
Tff7b72return Ta5d6ff"Ta5d6ffmoderateTa5d6ff"
Tff7b72if Te6edf3estimated_percent_per_hour Tff7b72< T79c0ff6.0Tb4b4b4:
Tff7b72return Ta5d6ff"Ta5d6ffhighTa5d6ff"
Tff7b72return Ta5d6ff"Ta5d6ffvery_highTa5d6ff"
Tff7b72def Td2a8ffestimate_battery_usageTb4b4b4(
Tff7b72*Tb4b4b4,
Te6edf3cpu_time_secondsTb4b4b4: Tffa657float Tff7b72| Tff7b72NoneTb4b4b4,
Te6edf3uptime_secondsTb4b4b4: Tffa657float Tff7b72| Tff7b72NoneTb4b4b4,
Te6edf3cpu_countTb4b4b4: Tffa657int Tff7b72| Tff7b72None Tff7b72= T79c0ff1Tb4b4b4,
Te6edf3on_batteryTb4b4b4: Tffa657bool Tff7b72| Tff7b72None Tff7b72= Tff7b72NoneTb4b4b4,
Te6edf3host_levelTb4b4b4: Tffa657int Tff7b72| Tff7b72None Tff7b72= Tff7b72NoneTb4b4b4,
Tb4b4b4) Tff7b72-Tff7b72> Tffa657dictTb4b4b4[Tffa657strTb4b4b4, Te6edf3AnyTb4b4b4] Tff7b72| Tff7b72NoneTb4b4b4:
T8b949e"""Build an estimated MeshChatX battery-usage payload.
avg_cpu_percent is percent of one logical core (may exceed 100 on
multi-threaded work). machine_share_percent normalizes by CPU count.
estimated_percent_per_hour is a rough battery pack drain rate.
"""
Tff7b72if Te6edf3cpu_time_seconds Tff7b72is Tff7b72None Tff7b72or Te6edf3uptime_seconds Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72return Tff7b72None
Tff7b72tryTb4b4b4:
Te6edf3cpu_time Tff7b72= Tffa657floatTb4b4b4(Te6edf3cpu_time_secondsTb4b4b4)
Te6edf3uptime Tff7b72= Tffa657floatTb4b4b4(Te6edf3uptime_secondsTb4b4b4)
Tff7b72except Tb4b4b4(Tf85149TypeErrorTb4b4b4, Tf85149ValueErrorTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Tff7b72if Te6edf3cpu_time Tff7b72< T79c0ff0 Tff7b72or Te6edf3uptime Tff7b72<Tff7b72= T79c0ff0Tb4b4b4:
Tff7b72return Tff7b72None
Te6edf3cores Tff7b72= T79c0ff1
Tff7b72if Te6edf3cpu_count Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3cores Tff7b72= Tffa657maxTb4b4b4(T79c0ff1Tb4b4b4, Tffa657intTb4b4b4(Te6edf3cpu_countTb4b4b4)Tb4b4b4)
Tff7b72except Tb4b4b4(Tf85149TypeErrorTb4b4b4, Tf85149ValueErrorTb4b4b4)Tb4b4b4:
Te6edf3cores Tff7b72= T79c0ff1
Te6edf3one_core_fraction Tff7b72= Te6edf3cpu_time Tff7b72/ Te6edf3uptime
Te6edf3avg_cpu_percent Tff7b72= Te6edf3one_core_fraction Tff7b72* T79c0ff100.0
Te6edf3machine_share_percent Tff7b72= Te6edf3_clampTb4b4b4(Tb4b4b4(Te6edf3one_core_fraction Tff7b72/ Te6edf3coresTb4b4b4) Tff7b72* T79c0ff100.0Tb4b4b4, T79c0ff0.0Tb4b4b4, T79c0ff100.0Tb4b4b4)
Te6edf3estimated_percent_per_hour Tff7b72= Tff7b72None
Te6edf3confidence Tff7b72= Ta5d6ff"Ta5d6ffwarming_upTa5d6ff"
Tff7b72if Te6edf3uptime Tff7b72>Tff7b72= Te6edf3_MIN_UPTIME_FOR_RATE_STb4b4b4:
Te6edf3estimated_percent_per_hour Tff7b72= Te6edf3_clampTb4b4b4(
Te6edf3one_core_fraction Tff7b72* Te6edf3_ONE_CORE_PERCENT_PER_HOURTb4b4b4,
T79c0ff0.0Tb4b4b4,
Te6edf3_MAX_PERCENT_PER_HOURTb4b4b4,
Tb4b4b4)
Te6edf3confidence Tff7b72= Ta5d6ff"Ta5d6ffestimateTa5d6ff"
Te6edf3intensity Tff7b72= Te6edf3drain_intensityTb4b4b4(Te6edf3estimated_percent_per_hourTb4b4b4)
Tff7b72return Tb4b4b4{
Ta5d6ff"Ta5d6ffavg_cpu_percentTa5d6ff"Tb4b4b4: Tffa657roundTb4b4b4(Te6edf3avg_cpu_percentTb4b4b4, T79c0ff1Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffmachine_share_percentTa5d6ff"Tb4b4b4: Tffa657roundTb4b4b4(Te6edf3machine_share_percentTb4b4b4, T79c0ff1Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffestimated_percent_per_hourTa5d6ff"Tb4b4b4: Tb4b4b4(
Tffa657roundTb4b4b4(Te6edf3estimated_percent_per_hourTb4b4b4, T79c0ff1Tb4b4b4)
Tff7b72if Te6edf3estimated_percent_per_hour Tff7b72is Tff7b72not Tff7b72None
Tff7b72else Tff7b72None
Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffintensityTa5d6ff"Tb4b4b4: Te6edf3intensityTb4b4b4,
Ta5d6ff"Ta5d6ffcpu_time_secondsTa5d6ff"Tb4b4b4: Tffa657roundTb4b4b4(Te6edf3cpu_timeTb4b4b4, T79c0ff2Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffuptime_secondsTa5d6ff"Tb4b4b4: Tffa657roundTb4b4b4(Te6edf3uptimeTb4b4b4, T79c0ff1Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffcpu_countTa5d6ff"Tb4b4b4: Te6edf3coresTb4b4b4,
Ta5d6ff"Ta5d6ffmethodTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffcpu_timeTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffconfidenceTa5d6ff"Tb4b4b4: Te6edf3confidenceTb4b4b4,
Ta5d6ff"Ta5d6ffon_batteryTa5d6ff"Tb4b4b4: Te6edf3on_batteryTb4b4b4,
Ta5d6ff"Ta5d6ffhost_levelTa5d6ff"Tb4b4b4: Te6edf3host_levelTb4b4b4,
Tb4b4b4}
Tff7b72def Td2a8ffread_linux_host_batteryTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657tupleTb4b4b4[Tffa657int Tff7b72| Tff7b72NoneTb4b4b4, Tffa657bool Tff7b72| Tff7b72NoneTb4b4b4]Tb4b4b4:
T8b949e"""Best-effort host pack reading from sysfs (Linux laptops and some SBCs)."""
Te6edf3power_root Tff7b72= Ta5d6ff"Ta5d6ff/sys/class/power_supplyTa5d6ff"
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisdirTb4b4b4(Te6edf3power_rootTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72NoneTb4b4b4, Tff7b72None
Tff7b72tryTb4b4b4:
Te6edf3entries Tff7b72= Tffa657sortedTb4b4b4(Te6edf3osTff7b72.Td2a8fflistdirTb4b4b4(Te6edf3power_rootTb4b4b4)Tb4b4b4)
Tff7b72except Tf85149OSErrorTb4b4b4:
Tff7b72return Tff7b72NoneTb4b4b4, Tff7b72None
Te6edf3level Tff7b72= Tff7b72None
Te6edf3charging Tff7b72= Tff7b72None
Tff7b72for Te6edf3name Tff7b72in Te6edf3entriesTb4b4b4:
Te6edf3base Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3power_rootTb4b4b4, Te6edf3nameTb4b4b4)
Te6edf3type_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3baseTb4b4b4, Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72with Tffa657openTb4b4b4(Te6edf3type_pathTb4b4b4, Te6edf3encodingTff7b72=Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4) Tff7b72as Te6edf3handleTb4b4b4:
Te6edf3supply_type Tff7b72= Te6edf3handleTff7b72.Td2a8ffreadTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149OSErrorTb4b4b4:
Tff7b72continue
Tff7b72if Te6edf3supply_type Tff7b72!= Ta5d6ff"Ta5d6ffbatteryTa5d6ff"Tb4b4b4:
Tff7b72continue
Te6edf3capacity_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3baseTb4b4b4, Ta5d6ff"Ta5d6ffcapacityTa5d6ff"Tb4b4b4)
Te6edf3status_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3baseTb4b4b4, Ta5d6ff"Ta5d6ffstatusTa5d6ff"Tb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72with Tffa657openTb4b4b4(Te6edf3capacity_pathTb4b4b4, Te6edf3encodingTff7b72=Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4) Tff7b72as Te6edf3handleTb4b4b4:
Te6edf3raw Tff7b72= Tffa657intTb4b4b4(Te6edf3handleTff7b72.Td2a8ffreadTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72if T79c0ff0 Tff7b72<Tff7b72= Te6edf3raw Tff7b72<Tff7b72= T79c0ff100Tb4b4b4:
Te6edf3level Tff7b72= Te6edf3raw
Tff7b72except Tb4b4b4(Tf85149OSErrorTb4b4b4, Tf85149ValueErrorTb4b4b4)Tb4b4b4:
Tff7b72pass
Tff7b72tryTb4b4b4:
Tff7b72with Tffa657openTb4b4b4(Te6edf3status_pathTb4b4b4, Te6edf3encodingTff7b72=Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4) Tff7b72as Te6edf3handleTb4b4b4:
Te6edf3status Tff7b72= Te6edf3handleTff7b72.Td2a8ffreadTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3status Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ffchargingTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fffullTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3charging Tff7b72= Tff7b72True
Tff7b72elif Te6edf3status Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ffdischargingTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffnot chargingTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3charging Tff7b72= Tff7b72False
Tff7b72except Tf85149OSErrorTb4b4b4:
Tff7b72pass
Tff7b72if Te6edf3level Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72break
Tff7b72return Te6edf3levelTb4b4b4, Te6edf3charging
Tff7b72class T56d364BatteryUsageTrackerTb4b4b4:
T8b949e"""Snapshot MeshChatX process CPU into a battery-usage estimate."""
Tff7b72def Tff7b72__init__Tb4b4b4(Tff7b72selfTb4b4b4) Tff7b72-Tff7b72> Tff7b72NoneTb4b4b4:
Tff7b72selfTff7b72.Td2a8ff_lastTb4b4b4: Tffa657dictTb4b4b4[Tffa657strTb4b4b4, Te6edf3AnyTb4b4b4] Tff7b72| Tff7b72None Tff7b72= Tff7b72None
Tff7b72def Td2a8ffsnapshotTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3processTb4b4b4: Te6edf3AnyTb4b4b4) Tff7b72-Tff7b72> Tffa657dictTb4b4b4[Tffa657strTb4b4b4, Te6edf3AnyTb4b4b4] Tff7b72| Tff7b72NoneTb4b4b4:
Tff7b72if Te6edf3process Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72return Tff7b72selfTff7b72.Td2a8ff_last
Tff7b72tryTb4b4b4:
Te6edf3times Tff7b72= Te6edf3processTff7b72.Td2a8ffcpu_timesTb4b4b4(Tb4b4b4)
Te6edf3cpu_time Tff7b72= Tffa657floatTb4b4b4(Tffa657getattrTb4b4b4(Te6edf3timesTb4b4b4, Ta5d6ff"Ta5d6ffuserTa5d6ff"Tb4b4b4, T79c0ff0.0Tb4b4b4)Tb4b4b4) Tff7b72+ Tffa657floatTb4b4b4(
Tffa657getattrTb4b4b4(Te6edf3timesTb4b4b4, Ta5d6ff"Ta5d6ffsystemTa5d6ff"Tb4b4b4, T79c0ff0.0Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Tff7b72selfTff7b72.Td2a8ff_last
Tff7b72tryTb4b4b4:
Te6edf3create_time Tff7b72= Tffa657floatTb4b4b4(Te6edf3processTff7b72.Td2a8ffcreate_timeTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Tff7b72selfTff7b72.Td2a8ff_last
Te6edf3uptime Tff7b72= Tffa657maxTb4b4b4(T79c0ff0.0Tb4b4b4, Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4) Tff7b72- Te6edf3create_timeTb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72import T7ee787psutil
Te6edf3cpu_count Tff7b72= Te6edf3psutilTff7b72.Td2a8ffcpu_countTb4b4b4(Te6edf3logicalTff7b72=Tff7b72TrueTb4b4b4) Tff7b72or T79c0ff1
Tff7b72except Tf85149ExceptionTb4b4b4:
Te6edf3cpu_count Tff7b72= T79c0ff1
Te6edf3host_levelTb4b4b4, Te6edf3charging Tff7b72= Te6edf3read_linux_host_batteryTb4b4b4(Tb4b4b4)
Te6edf3on_battery Tff7b72= Tff7b72None
Tff7b72if Te6edf3charging Tff7b72is Tff7b72TrueTb4b4b4:
Te6edf3on_battery Tff7b72= Tff7b72False
Tff7b72elif Te6edf3charging Tff7b72is Tff7b72FalseTb4b4b4:
Te6edf3on_battery Tff7b72= Tff7b72True
Te6edf3estimate Tff7b72= Te6edf3estimate_battery_usageTb4b4b4(
Te6edf3cpu_time_secondsTff7b72=Te6edf3cpu_timeTb4b4b4,
Te6edf3uptime_secondsTff7b72=Te6edf3uptimeTb4b4b4,
Te6edf3cpu_countTff7b72=Te6edf3cpu_countTb4b4b4,
Te6edf3on_batteryTff7b72=Te6edf3on_batteryTb4b4b4,
Te6edf3host_levelTff7b72=Te6edf3host_levelTb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3estimate Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72selfTff7b72.Td2a8ff_last Tff7b72= Te6edf3estimate
Tff7b72return Te6edf3estimate
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────